import logging
import os
from datetime import datetime
from typing import Optional, Dict, Any, Union
from zoneinfo import ZoneInfo

import requests

LOGGER = logging.getLogger(__name__)


def _check_required_params(*args) -> bool:
    """
    检查必要参数是否存在
    :param args: 需要检查的参数
    :return: 如果所有参数都存在返回 True，否则返回 False 并记录错误日志
    """
    for arg in args:
        if arg is None or (isinstance(arg, (str, int, float)) and not arg):
            param_name = next((k for k, v in locals().items() if v == arg and k != 'args'), 'unknown')
            LOGGER.error(f"{param_name} 为空，无法请求接口")
            return False
    return True


class AutomationPlatform:
    def __init__(self):
        """
        初始化请求客户端
        """
        self.task_id: Optional[Union[str, int]] = None
        self.upload_data_url: Optional[str] = None
        self.upload_file_url: Optional[str] = None
        self.file_name: Optional[str] = None
        self.file_path: Optional[str] = None
        self.session = requests.Session()

    def set_task_id(self, task_id: Union[str, int]) -> None:
        """
        设置任务 ID
        :param task_id: 任务 ID
        """
        self.task_id = task_id

    def set_upload_data_url(self, upload_data_url: str) -> None:
        """
        设置上传数据的 URL
        :param upload_data_url: 上传数据的 URL
        """
        self.upload_data_url = upload_data_url

    def set_upload_file_url(self, upload_file_url: str) -> None:
        """
        设置上传文件的 URL
        :param upload_file_url: 上传文件的 URL
        """
        self.upload_file_url = upload_file_url

    def set_file_name(self, file_name: str) -> None:
        """
        设置文件名称
        :param file_name: 文件名称
        """
        self.file_name = file_name

    def set_file_path(self, file_path: str) -> None:
        """
        设置文件路径
        :param file_path: 文件路径
        """
        self.file_path = file_path

    def post_request(self, url: str, data: Optional[Union[Dict[str, Any], list]] = None,
                     json: Optional[Dict[str, Any]] = None,
                     headers: Optional[Dict[str, str]] = None) -> Optional[requests.Response]:
        """
        发送普通 POST 请求

        :param url: 请求的 URL
        :param data: 表单数据，字典或元组列表
        :param json: JSON 数据
        :param headers: 请求头，字典类型
        :return: 响应对象或 None（请求出错时）
        """
        try:
            response = self.session.post(url, data=data, json=json, headers=headers)
            response.raise_for_status()  # 检查响应状态码
            return response
        except requests.RequestException as e:
            LOGGER.error(f"普通 POST 请求出错: {url}, 错误信息: {e}", exc_info=True)
            return None

    def post_file_upload(self, url: str, file_path: str, file_name: str, file_field_name: str = "file",
                         data: Optional[Union[Dict[str, Any], list]] = None,
                         headers: Optional[Dict[str, str]] = None) -> Optional[requests.Response]:
        """
        发送文件上传的 POST 请求

        :param url: 请求的 URL
        :param file_path: 要上传的文件路径
        :param file_name: 文件名称
        :param file_field_name: 文件字段名，默认为 "file"
        :param data: 其他表单数据，字典或元组列表
        :param headers: 请求头，字典类型
        :return: 响应对象或 None（请求出错时）
        """
        if not os.path.exists(file_path) or not os.path.isfile(file_path):
            LOGGER.error(f"文件未找到或不是有效的文件: {file_path}")
            return None

        try:
            with open(file_path, "rb") as file:
                files = {file_field_name: (file_name, file)}
                # 若有其他表单数据，将其转换为元组列表
                if data and isinstance(data, dict):
                    data = [(key, value) for key, value in data.items()]
                response = self.session.post(url, files=files, data=data, headers=headers)
                response.raise_for_status()  # 检查响应状态码
                return response
        except requests.RequestException as e:
            LOGGER.error(f"文件上传 POST 请求出错: {url}, 错误信息: {e}", exc_info=True)
            return None

    async def _send_status_update(self, url: str, payload: Dict[str, Any]) -> None:
        """
        发送状态更新请求

        :param url: 请求的 URL
        :param payload: 请求的 JSON 数据
        """
        if not _check_required_params(url, payload):
            return

        try:
            LOGGER.info("上传数据接口，URL: %s", url)
            LOGGER.info("上传数据接口请求参数: %s", payload)
            response = self.post_request(url=url, json=payload)
            if response:
                LOGGER.info("上传数据接口请求完成，响应状态码: %s", response.status_code)
            else:
                LOGGER.error("上传数据接口请求失败，未获取到有效响应")
        except requests.RequestException as e:
            LOGGER.error("请求上传数据接口时出错: %s", str(e), exc_info=True)

    async def start_run(self) -> None:
        """
        开始运行
        """
        if not _check_required_params(self.task_id, self.upload_data_url):
            return

        payload = {
            "crawlerTaskId": str(self.task_id),
            "status": "RUNNING",
            "progress": 0
        }
        await self._send_status_update(self.upload_data_url, payload)

    async def process_run(self, progress: int) -> None:
        """
        过程运行

        :param progress: 当前进度
        """
        if not _check_required_params(progress, self.task_id, self.upload_data_url):
            return

        payload = {
            "crawlerTaskId": str(self.task_id),
            "status": "RUNNING",
            "progress": progress
        }
        await self._send_status_update(self.upload_data_url, payload)

    async def end_run(self, status: str, duration: int, progress: int,
                      failure_reason: str) -> None:
        """
        结束运行

        :param status: 任务状态
        :param duration: 任务持续时间
        :param progress: 任务进度
        :param failure_reason: 失败原因，可选
        """
        if not _check_required_params(status, duration, progress, self.task_id, self.upload_data_url,
                                      self.upload_file_url):
            return

        current_date_str = datetime.now(ZoneInfo("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
        LOGGER.info("当前时间: %s", current_date_str)

        payload = {
            "crawlerTaskId": str(self.task_id),
            "endTime": current_date_str,
            "status": status,
            "duration": duration,
            "progress": progress,
            "failureReason": failure_reason
        }
        await self._send_status_update(self.upload_data_url, payload)

        if os.path.exists(self.file_path) and os.path.isfile(self.file_path):
            try:
                LOGGER.info("上传文件接口，URL: %s，文件路径: %s", self.upload_file_url, self.file_path)
                data = {
                    "crawlerTaskId": str(self.task_id),
                }
                LOGGER.info("上传文件接口请求参数: %s", data)
                response = self.post_file_upload(url=self.upload_file_url, file_path=self.file_path,
                                                 file_name=self.file_name, data=data)
                if response:
                    LOGGER.info("上传文件接口请求完成，响应状态码: %s", response.status_code)
                else:
                    LOGGER.error("上传文件接口请求失败，未获取到有效响应")
            except requests.RequestException as e:
                LOGGER.error("请求上传文件接口时出错: %s", str(e), exc_info=True)
        else:
            LOGGER.warning("文件 %s 不存在或不是文件，跳过文件上传", self.file_path)


global_automation_platform = AutomationPlatform()
